use crate::{PathCommand, Point, Transform, Transformation};

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum Verb {
	MoveTo,
	LineTo,
	QuadraticTo,
	CubicTo,
	Close,
}

/// A sequence of commands that defines a set of contours, each of which consists of a sequence of
/// curve segments. Each contour is either open or closed.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Path {
	verbs: Vec<Verb>,
	points: Vec<Point>,
}
impl Path {
	/// Creates a new empty path.
	pub fn new() -> Path {
		Path::default()
	}

	/// Returns a slice of the points that make up `self`.
	pub fn points(&self) -> &[Point] {
		&self.points
	}

	/// Returns a mutable slice of the points that make up `self`.
	pub fn points_mut(&mut self) -> &mut [Point] {
		&mut self.points
	}

	/// Adds a new contour, starting at the given point.
	pub fn move_to(&mut self, p: Point) {
		self.verbs.push(Verb::MoveTo);
		self.points.push(p);
	}

	/// Adds a line segment to the current contour, starting at the current point.
	pub fn line_to(&mut self, p: Point) {
		self.verbs.push(Verb::LineTo);
		self.points.push(p);
	}

	// Adds a quadratic Bezier curve segment to the current contour, starting at the current point.
	pub fn quadratic_to(&mut self, p1: Point, p: Point) {
		self.verbs.push(Verb::QuadraticTo);
		self.points.push(p1);
		self.points.push(p);
	}

	// Adds a quadratic Bezier curve segment to the current contour, starting at the current point.
	pub fn cubic_to(&mut self, p1: Point, p2: Point, p: Point) {
		self.verbs.push(Verb::CubicTo);
		self.points.push(p1);
		self.points.push(p2);
		self.points.push(p);
	}

	/// Closes the current contour.
	pub fn close(&mut self) {
		self.verbs.push(Verb::Close);
	}

	/// Clears `self`.
	pub fn clear(&mut self) {
		self.verbs.clear();
		self.points.clear();
	}
}
impl Transform for Path {
	fn transform(mut self, t: &impl Transformation) -> Self {
		self.transform_mut(t);
		self
	}
	fn transform_mut(&mut self, t: &impl Transformation) {
		for point in self.points_mut() {
			point.transform_mut(t);
		}
	}
}

impl Path {
	/// Returns an iterator over the commands that make up `self`.
	pub fn commands(&self) -> Commands {
		Commands {
			verbs: self.verbs.iter(),
			points: self.points.iter(),
		}
	}
}
/// An iterator over the commands that make up a path.
#[derive(Clone, Debug)]
pub struct Commands<'a> {
	verbs: core::slice::Iter<'a, Verb>,
	points: core::slice::Iter<'a, Point>,
}
impl<'a> Commands<'a> {
	fn next_point(&mut self) -> Point {
		*self.points.next().unwrap()
	}
}
impl<'a> Iterator for Commands<'a> {
	type Item = PathCommand;
	fn next(&mut self) -> Option<PathCommand> {
		self.verbs.next().map(|verb| match verb {
			Verb::MoveTo => PathCommand::MoveTo(self.next_point()),
			Verb::LineTo => PathCommand::LineTo(self.next_point()),
			Verb::QuadraticTo => PathCommand::QuadraticTo(self.next_point(), self.next_point()),
			Verb::CubicTo => PathCommand::CubicTo(self.next_point(), self.next_point(), self.next_point()),
			Verb::Close => PathCommand::Close,
		})
	}
}

// use crate::iter::{FromInternalIterator, InternalIterator, IntoInternalIterator};
// impl ExtendFromInternalIterator<PathCommand> for Path {
// 	fn extend_from_internal_iter<I>(&mut self, internal_iter: I)
// 	where
// 		I: IntoInternalIterator<Item = PathCommand>,
// 	{
// 		internal_iter.into_internal_iter().for_each(&mut |command| {
// 			match command {
// 				PathCommand::MoveTo(p) => self.move_to(p),
// 				PathCommand::LineTo(p) => self.line_to(p),
// 				PathCommand::QuadraticTo(p1, p) => self.quadratic_to(p1, p),
// 				PathCommand::CubicTo(p1, p2, p) => self.cubic_to(p1, p2, p),
// 				PathCommand::Close => self.close(),
// 			}
// 			true
// 		});
// 	}
// }
// impl FromInternalIterator<PathCommand> for Path {
// 	fn from_internal_iter<I>(internal_iter: I) -> Self
// 	where
// 		I: IntoInternalIterator<Item = PathCommand>,
// 	{
// 		let mut path = Path::new();
// 		path.extend_from_internal_iter(internal_iter);
// 		path
// 	}
// }
